import os
import sys

import ezdxf
import numpy as np
import pandas as pd
from scipy.spatial import KDTree


def read_points(flags, file_path):
    df = pd.read_csv(file_path, header=None, delimiter='\t', names=['点号', 'X', 'Y', 'Z'])
    if flags == '是':
        numpy_arrays = df[['Y', 'X', 'Z']].to_numpy()
    else:
        numpy_arrays = df[['X', 'Y', 'Z']].to_numpy()
    return numpy_arrays


# 输出所有图层名称
def print_all_layers(filename):
    doc = ezdxf.readfile(filename)
    for layer in doc.layers:
        name = layer.dxf.name
        first_char = name[0]
        i = ord(first_char)
        if (65 <= i <= 90) or (97 <= i <= 122):
            print(name)
        else:
            print('a' + name)


# 遍历获取点
def get_point_from_dxf(filepath):
    dwg_points = []
    dwg = ezdxf.readfile(filepath)
    modelspace = dwg.modelspace()
    for layer in dwg.layers:
        if layer.dxf.name == 'mainlayer':
            for e in modelspace:
                if e.dxftype() == 'POINT':
                    point = e.dxf.location
                    dwg_points.append(point)
    return dwg_points


# 计算两点的欧式距离
def euclidean_distance(point1, point2):
    return np.linalg.norm(point1 - point2)


# 将点添加到dataframe中
def add_point_to_df(dataframe, src_point, check_point, error):
    dataframe.loc[len(dataframe.index)] = [src_point[1], src_point[0], src_point[2],
                                           check_point[1], check_point[0], check_point[2],
                                           error, '']
    return dataframe


# 寻找最近点
def find_nearest_points(target_points, data_points):
    # 构建KD树
    tree = KDTree(data_points)

    # 查询每个目标点对应的最近点的索引
    nearest_indices = tree.query(target_points)[1]

    # 创建一个空的DataFrame，
    df = pd.DataFrame(
        columns=['原始点X', '原始点Y', '原始点Z', '检查点X', '检查点Y', '检查点Z', '较差', '中误差'])

    # 输出每个目标点对应的最近点的信息和差值
    for i, nearest_index in enumerate(nearest_indices):
        nearest_point = data_points[nearest_index]
        distance = euclidean_distance(nearest_point, target_points[i])
        add_point_to_df(df, nearest_point, target_points[i], round(distance, 5))
    median_error = df['较差'].std() / np.sqrt(len(df))
    df.loc[0, '中误差'] = round(median_error, 5)

    return df


def result_output(data, output_path):
    excel_path = output_path + "/测绘成品质量检查精度.xlsx"
    # 判断文件是否存在
    if os.path.isfile(excel_path):
        # 文件存在，删除原文件
        os.remove(excel_path)
    # 将数据写入excel文件
    data.to_excel(excel_path, index=False)


if __name__ == '__main__':
    check_point_path = "data/检测点数据.txt"
    dxf_path = "data/成果数据.dxf"
    flag = '是'
    output_filepath = "result/测绘成品质量检查精度.xlsx"

    # 获取检查点
    check_points = read_points(flag, check_point_path)
    # 打印dwg数据所有图层
    print_all_layers(dxf_path)
    # 从dwg数据中获取点数据
    dxf_points = get_point_from_dxf(dxf_path)
    dxf_points = np.array(dxf_points)

    result = find_nearest_points(check_points, dxf_points)
    result_output(result, output_filepath)
